use crate::metrics::{ComplexityMetrics, HalsteadMetrics};
use codegraph_core::{SyntaxNode, Result as CoreResult};
use std::collections::{HashMap, HashSet};
use tracing::{debug};

/// Complexity analyzer for code
pub struct ComplexityAnalyzer {
    /// Configuration for complexity analysis
    config: ComplexityConfig,
    /// Cache for complexity calculations
    cache: HashMap<String, ComplexityMetrics>,
}

/// Configuration for complexity analysis
#[derive(Debug, Clone)]
pub struct ComplexityConfig {
    /// Enable cyclomatic complexity calculation
    pub enable_cyclomatic: bool,
    /// Enable cognitive complexity calculation
    pub enable_cognitive: bool,
    /// Enable Halstead complexity calculation
    pub enable_halstead: bool,
    /// Maximum depth to analyze
    pub max_depth: usize,
    /// Language-specific operators
    pub operators: HashMap<String, Vec<String>>,
}

impl Default for ComplexityConfig {
    fn default() -> Self {
        let mut operators = HashMap::new();
        
        // Python operators
        operators.insert("python".to_string(), vec![
            "+".to_string(), "-".to_string(), "*".to_string(), "/".to_string(),
            "//".to_string(), "%".to_string(), "**".to_string(),
            "==".to_string(), "!=".to_string(), "<".to_string(), ">".to_string(),
            "<=".to_string(), ">=".to_string(), "and".to_string(), "or".to_string(),
            "not".to_string(), "in".to_string(), "is".to_string(),
            "=".to_string(), "+=".to_string(), "-=".to_string(), "*=".to_string(),
        ]);

        // JavaScript/TypeScript operators
        operators.insert("javascript".to_string(), vec![
            "+".to_string(), "-".to_string(), "*".to_string(), "/".to_string(),
            "%".to_string(), "**".to_string(), "++".to_string(), "--".to_string(),
            "==".to_string(), "!=".to_string(), "===".to_string(), "!==".to_string(),
            "<".to_string(), ">".to_string(), "<=".to_string(), ">=".to_string(),
            "&&".to_string(), "||".to_string(), "!".to_string(),
            "=".to_string(), "+=".to_string(), "-=".to_string(), "*=".to_string(),
        ]);

        // Rust operators
        operators.insert("rust".to_string(), vec![
            "+".to_string(), "-".to_string(), "*".to_string(), "/".to_string(),
            "%".to_string(), "==".to_string(), "!=".to_string(),
            "<".to_string(), ">".to_string(), "<=".to_string(), ">=".to_string(),
            "&&".to_string(), "||".to_string(), "!".to_string(),
            "=".to_string(), "+=".to_string(), "-=".to_string(), "*=".to_string(),
            "->".to_string(), "=>".to_string(), "::".to_string(),
        ]);

        Self {
            enable_cyclomatic: true,
            enable_cognitive: true,
            enable_halstead: true,
            max_depth: 50,
            operators,
        }
    }
}

impl ComplexityAnalyzer {
    /// Create a new complexity analyzer
    pub fn new(config: ComplexityConfig) -> Self {
        Self {
            config,
            cache: HashMap::new(),
        }
    }

    /// Analyze complexity of a syntax tree
    pub async fn analyze_complexity(
        &mut self,
        root: &dyn SyntaxNode,
        language: &str,
    ) -> CoreResult<ComplexityMetrics> {
        debug!("Analyzing complexity for language: {}", language);

        let mut metrics = ComplexityMetrics::default();

        // Calculate cyclomatic complexity
        if self.config.enable_cyclomatic {
            metrics.cyclomatic_complexity = self.calculate_cyclomatic_complexity(root).await?;
        }

        // Calculate cognitive complexity
        if self.config.enable_cognitive {
            metrics.cognitive_complexity = self.calculate_cognitive_complexity(root).await?;
        }

        // Calculate Halstead complexity
        if self.config.enable_halstead {
            metrics.halstead_complexity = self.calculate_halstead_complexity(root, language).await?;
        }

        // Calculate nesting depth
        let (max_depth, avg_depth) = self.calculate_nesting_depth(root).await?;
        metrics.max_nesting_depth = max_depth;
        metrics.avg_nesting_depth = avg_depth;

        // Calculate function-level complexity
        metrics.function_complexity = self.calculate_function_complexity(root).await?;

        debug!("Complexity analysis complete: cyclomatic={}, cognitive={}, max_depth={}",
               metrics.cyclomatic_complexity, metrics.cognitive_complexity, metrics.max_nesting_depth);

        Ok(metrics)
    }

    /// Calculate cyclomatic complexity
    async fn calculate_cyclomatic_complexity(&self, root: &dyn SyntaxNode) -> CoreResult<f64> {
        let mut complexity = 1.0; // Base complexity

        self.traverse_for_cyclomatic(root, &mut complexity).await?;

        Ok(complexity)
    }

    /// Traverse tree for cyclomatic complexity calculation
    async fn traverse_for_cyclomatic(&self, node: &dyn SyntaxNode, complexity: &mut f64) -> CoreResult<()> {
        let node_kind = node.kind();

        // Decision points that increase cyclomatic complexity
        match node_kind {
            "if_statement" | "elif_clause" | "else_clause" => *complexity += 1.0,
            "for_statement" | "while_statement" | "do_while_statement" => *complexity += 1.0,
            "switch_statement" | "case_clause" | "match_expression" => *complexity += 1.0,
            "try_statement" | "catch_clause" | "except_clause" => *complexity += 1.0,
            "conditional_expression" | "ternary_expression" => *complexity += 1.0,
            "logical_and" | "logical_or" | "and_expression" | "or_expression" => *complexity += 1.0,
            _ => {}
        }

        // Recursively process children
        for child in node.children() {
            Box::pin(self.traverse_for_cyclomatic(&*child, complexity)).await?;
        }

        Ok(())
    }

    /// Calculate cognitive complexity
    async fn calculate_cognitive_complexity(&self, root: &dyn SyntaxNode) -> CoreResult<f64> {
        let mut complexity = 0.0;
        let mut nesting_level = 0;

        self.traverse_for_cognitive(root, &mut complexity, &mut nesting_level).await?;

        Ok(complexity)
    }

    /// Traverse tree for cognitive complexity calculation
    async fn traverse_for_cognitive(
        &self,
        node: &dyn SyntaxNode,
        complexity: &mut f64,
        nesting_level: &mut usize,
    ) -> CoreResult<()> {
        let node_kind = node.kind();
        let mut increment_nesting = false;

        // Cognitive complexity rules
        match node_kind {
            "if_statement" => {
                *complexity += 1.0 + *nesting_level as f64;
                increment_nesting = true;
            }
            "elif_clause" | "else_clause" => {
                *complexity += 1.0;
            }
            "for_statement" | "while_statement" | "do_while_statement" => {
                *complexity += 1.0 + *nesting_level as f64;
                increment_nesting = true;
            }
            "switch_statement" | "match_expression" => {
                *complexity += 1.0 + *nesting_level as f64;
                increment_nesting = true;
            }
            "case_clause" => {
                *complexity += 1.0;
            }
            "try_statement" => {
                increment_nesting = true;
            }
            "catch_clause" | "except_clause" => {
                *complexity += 1.0 + *nesting_level as f64;
            }
            "conditional_expression" | "ternary_expression" => {
                *complexity += 1.0 + *nesting_level as f64;
            }
            "logical_and" | "logical_or" => {
                *complexity += 1.0;
            }
            "break_statement" | "continue_statement" => {
                *complexity += 1.0;
            }
            "function_definition" | "method_definition" => {
                // Functions create their own scope
                increment_nesting = true;
            }
            _ => {}
        }

        // Process children with updated nesting level
        if increment_nesting {
            *nesting_level += 1;
        }

        for child in node.children() {
            Box::pin(self.traverse_for_cognitive(child.as_ref(), complexity, nesting_level)).await?;
        }

        if increment_nesting {
            *nesting_level -= 1;
        }

        Ok(())
    }

    /// Calculate Halstead complexity metrics
    async fn calculate_halstead_complexity(
        &self,
        root: &dyn SyntaxNode,
        language: &str,
    ) -> CoreResult<HalsteadMetrics> {
        let mut operators = HashSet::new();
        let mut operands = HashSet::new();
        let mut operator_count = HashMap::new();
        let mut operand_count = HashMap::new();

        let language_operators = self.config.operators.get(language).cloned().unwrap_or_default();

        self.traverse_for_halstead(
            root,
            &language_operators,
            &mut operators,
            &mut operands,
            &mut operator_count,
            &mut operand_count,
        ).await?;

        let distinct_operators = operators.len();
        let distinct_operands = operands.len();
        let total_operators: usize = operator_count.values().sum();
        let total_operands: usize = operand_count.values().sum();

        let vocabulary = distinct_operators + distinct_operands;
        let length = total_operators + total_operands;

        let calculated_length = if distinct_operators > 0 && distinct_operands > 0 {
            (distinct_operators as f64) * (distinct_operators as f64).log2() +
            (distinct_operands as f64) * (distinct_operands as f64).log2()
        } else {
            0.0
        };

        let volume = if vocabulary > 0 {
            (length as f64) * (vocabulary as f64).log2()
        } else {
            0.0
        };

        let difficulty = if distinct_operands > 0 && total_operands > 0 {
            ((distinct_operators as f64) / 2.0) * ((total_operands as f64) / (distinct_operands as f64))
        } else {
            0.0
        };

        let effort = volume * difficulty;
        let time = effort / 18.0; // Stroud number
        let bugs = volume / 3000.0; // Empirical constant

        Ok(HalsteadMetrics {
            distinct_operators,
            distinct_operands,
            total_operators,
            total_operands,
            vocabulary,
            length,
            calculated_length,
            volume,
            difficulty,
            effort,
            time,
            bugs,
        })
    }

    /// Traverse tree for Halstead metrics calculation
    async fn traverse_for_halstead(
        &self,
        node: &dyn SyntaxNode,
        language_operators: &[String],
        operators: &mut HashSet<String>,
        operands: &mut HashSet<String>,
        operator_count: &mut HashMap<String, usize>,
        operand_count: &mut HashMap<String, usize>,
    ) -> CoreResult<()> {
        let node_kind = node.kind();
        let node_text = node.text();

        // Classify node as operator or operand
        if self.is_operator(node_kind, &node_text, language_operators) {
            operators.insert(node_text.to_string());
            *operator_count.entry(node_text.to_string()).or_insert(0) += 1;
        } else if self.is_operand(node_kind, &node_text) {
            operands.insert(node_text.to_string());
            *operand_count.entry(node_text.to_string()).or_insert(0) += 1;
        }

        // Process children
        for child in node.children() {
            Box::pin(self.traverse_for_halstead(
                child.as_ref(),
                language_operators,
                operators,
                operands,
                operator_count,
                operand_count,
            )).await?;
        }

        Ok(())
    }

    /// Check if a node represents an operator
    fn is_operator(&self, node_kind: &str, node_text: &str, language_operators: &[String]) -> bool {
        // Check if it's in the language-specific operators list
        if language_operators.contains(&node_text.to_string()) {
            return true;
        }

        // Check common operator node types
        matches!(node_kind,
            "binary_operator" | "unary_operator" | "assignment_operator" |
            "comparison_operator" | "logical_operator" | "arithmetic_operator" |
            "bitwise_operator" | "compound_assignment" | "increment" | "decrement"
        )
    }

    /// Check if a node represents an operand
    fn is_operand(&self, node_kind: &str, _node_text: &str) -> bool {
        matches!(node_kind,
            "identifier" | "number" | "string" | "boolean" | "null" |
            "variable" | "constant" | "literal" | "field_access" |
            "array_access" | "function_call" | "method_call"
        )
    }

    /// Calculate nesting depth
    async fn calculate_nesting_depth(&self, root: &dyn SyntaxNode) -> CoreResult<(usize, f64)> {
        let mut max_depth = 0;
        let mut total_depth = 0;
        let mut node_count = 0;

        self.traverse_for_depth(root, 0, &mut max_depth, &mut total_depth, &mut node_count).await?;

        let avg_depth = if node_count > 0 {
            total_depth as f64 / node_count as f64
        } else {
            0.0
        };

        Ok((max_depth, avg_depth))
    }

    /// Traverse tree for depth calculation
    async fn traverse_for_depth(
        &self,
        node: &dyn SyntaxNode,
        current_depth: usize,
        max_depth: &mut usize,
        total_depth: &mut usize,
        node_count: &mut usize,
    ) -> CoreResult<()> {
        *max_depth = (*max_depth).max(current_depth);
        *total_depth += current_depth;
        *node_count += 1;

        let node_kind = node.kind();
        let increment_depth = matches!(node_kind,
            "if_statement" | "for_statement" | "while_statement" | "function_definition" |
            "class_definition" | "try_statement" | "with_statement" | "block"
        );

        let next_depth = if increment_depth {
            current_depth + 1
        } else {
            current_depth
        };

        for child in node.children() {
            Box::pin(self.traverse_for_depth(child.as_ref(), next_depth, max_depth, total_depth, node_count)).await?;
        }

        Ok(())
    }

    /// Calculate function-level complexity
    async fn calculate_function_complexity(&self, root: &dyn SyntaxNode) -> CoreResult<HashMap<String, f64>> {
        let mut function_complexity = HashMap::new();

        self.traverse_for_functions(root, &mut function_complexity).await?;

        Ok(function_complexity)
    }

    /// Traverse tree for function complexity calculation
    async fn traverse_for_functions(
        &self,
        node: &dyn SyntaxNode,
        function_complexity: &mut HashMap<String, f64>,
    ) -> CoreResult<()> {
        let node_kind = node.kind();

        if matches!(node_kind, "function_definition" | "method_definition" | "function_declaration") {
            // Extract function name
            let function_name = self.extract_function_name(node).unwrap_or_else(|| "anonymous".to_string());
            
            // Calculate complexity for this function
            let complexity = self.calculate_cyclomatic_complexity(node).await?;
            function_complexity.insert(function_name, complexity);
        }

        // Process children
        for child in node.children() {
            Box::pin(self.traverse_for_functions(child.as_ref(), function_complexity)).await?;
        }

        Ok(())
    }

    /// Extract function name from a function node
    fn extract_function_name(&self, node: &dyn SyntaxNode) -> Option<String> {
        // Look for identifier child that represents the function name
        for child in node.children() {
            if child.kind() == "identifier" {
                return Some(child.text().to_string());
            }
        }
        None
    }
}
